package org.zh.sh.util.http;


import org.zh.sh.util.Utils;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;

public class MultipartInput {

    private static final byte[] CRLF2 = "\r\n\r\n".getBytes();

    private static final String CRLF = "\r\n";

    private static final byte[] content = "Content-Disposition: form-data; name=\"".getBytes();

    private static final String type = "Content-Type: ";

    private static final String filename = "\"; filename=\"";

    InputStream in;

    byte[] boundary;

    private static List<byte[]> splitByBoundary(byte[] data, byte[] boundary) {
        List<byte[]> result = new ArrayList<>();
        if (data == null || data.length == 0) {
            return result;
        }
        int boundaryLength = boundary.length;
        int startIndex = 0;
        int currentIndex = 0;
        int boundaryIndex = 0;
        while (currentIndex < data.length) {
            if (data[currentIndex] == boundary[boundaryIndex]) {
                if (boundaryIndex == boundaryLength - 1) {
                    if (currentIndex > boundaryLength) {
                        result.add(Arrays.copyOfRange(data, startIndex, currentIndex - boundaryLength-1));
                    }
                    boundaryIndex = 0;
                    startIndex = currentIndex+1;
                }else{
                    boundaryIndex++;
                }
            } else {
                boundaryIndex = 0;
            }
            currentIndex++;
        }
        if (startIndex < data.length - (boundaryLength + 4)) {
            result.add(Arrays.copyOfRange(data, startIndex, data.length - (boundaryLength + 4)));
        }
        return result;
    }

    private static List<byte[]> splitByBoundary2(byte[] data) {
        List<byte[]> result = new ArrayList<>();
        int boundaryLength = MultipartInput.CRLF2.length;
        int startIndex = 0;
        int currentIndex = 0;
        int boundaryIndex = 0;
            while (currentIndex < data.length) {
                if (data[currentIndex] == MultipartInput.CRLF2[boundaryIndex]) {
                    if (boundaryIndex == boundaryLength - 1) {
                        if (currentIndex > boundaryLength) {
                            result.add(Arrays.copyOfRange(data, startIndex, currentIndex - boundaryLength+1));
                            startIndex = currentIndex+1;
                            break;
                        }
                        boundaryIndex = 0;
                        startIndex = currentIndex+1;
                    }else{
                        boundaryIndex++;
                    }
                } else {
                    boundaryIndex = 0;
                }
                currentIndex++;
            }
        if (startIndex <= data.length) {
            result.add(Arrays.copyOfRange(data, startIndex, data.length));
        }
        return result;
    }

    public Map<String, Object> run() throws IOException {
        Map<String,Object> data = new HashMap<>();
        List<byte[]> all = splitByBoundary(in.readAllBytes(), boundary);
        int contentLength = content.length;
        for (byte[] bytes : all) {
            List<byte[]> bytes1 = splitByBoundary2(bytes);
            byte[] first = bytes1.getFirst();
            byte[] last = bytes1.getLast();
            String name = new String(first, contentLength, first.length - contentLength);
            int i1 = name.indexOf(filename);
            if (i1 > 0) {
                int i2 = name.indexOf(type);
                String oldName = name.substring(i1 + filename.length(), i2 - 3);
                String newName = UUID.randomUUID() + oldName.substring(oldName.lastIndexOf("."));
                Path path = Files.write(Paths.get(System.getProperty("user.dir"), "upload", newName), last, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
                Multipart multipart = new Multipart(name.substring(0, i1),
                        name.substring(i2 + type.length()),
                        oldName,
                        newName, path.toString());
                data.put(multipart.inputName(), multipart);
            } else {
                data.put(name.substring(0, name.length() - 1), new String(last));
            }
        }
        return data;
    }

    public static MultipartInput getInstance(InputStream inputStream, String boundary) {
        if (inputStream == null || boundary == null || boundary.isEmpty()) {
            throw new IllegalArgumentException("boundary inputStream  is not null and is not empty");
        }
        MultipartInput input = new MultipartInput();
        input.boundary = ("--" + boundary + CRLF).getBytes();
        input.in = inputStream;
        return input;
    }
}